/**
**************************************************************************************
*Founder Effect                                                                      *
*Copyright (C) 2006-2007, 5d Studios (www.5d-Studios.com)                            *
*                                                                                    *
*This library is free software; you can redistribute it and/or                       *
*modify it under the terms of the GNU Lesser General Public                          *
*License as published by the Free Software Foundation; either                        *
*version 2.1 of the License, or (at your option) any later version.                  *
*                                                                                    *
*This library is distributed in the hope that it will be useful,                     *
*but WITHOUT ANY WARRANTY; without even the implied warranty of                      *
*MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU                   *
*Lesser General Public License for more details.                                     *
*                                                                                    *
*You should have received a copy of the GNU Lesser General Public                    *
*License along with this library; if not, write to the Free Software                 *
*Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA      *
**************************************************************************************
*/
#include "StdAfx.h"
#include "Recorder.h"
#include "game/PlayerController.h"
#include "game/EntityEvents.h"
#include "shared/MFile.h"
#include <fstream>
#include <sstream>
#include "kernel.h"

extern Kernel* g_kernel;

/*-------------------------------------------------------------------
    Recording Functions 
--------------------------------------------------------------------*/

Recorder::Recorder() : IEventListener()
{
    m_record = false;
    m_playing = false;
    m_startTime = 0.0f;
    m_deltaTime = 0.0f;
    m_nextTime  = 0.0f;
    m_oldTime   = 0.0f;

    record_f.Set( this );
    playrec_f.Set( this );
    stoprec_f.Set( this );
    g_kernel->GetConsole()->AddCommand( &record_f, "record" );
    g_kernel->GetConsole()->AddCommand( &playrec_f, "playdemo" );
    g_kernel->GetConsole()->AddCommand( &stoprec_f, "stopdemo" );

    EventManager::GetInstance()->AddListener(this); 
}

Recorder::~Recorder()
{
    IEventListener::~IEventListener();

    g_kernel->GetConsole()->RemoveCommand( "record" );
    g_kernel->GetConsole()->RemoveCommand( "playdemo" );
    g_kernel->GetConsole()->RemoveCommand( "stopdemo" );

    EventManager::GetInstance()->RemoveListener(this);
}

/** Handle an event */
bool Recorder::HandleEvent( IEvent* e )
{
    // if recording save the events
    if ( m_record ) 
    {
        //if ( e->GetData< EntityEvents >()->ent_id == 0 )
        //{
        IEvent stored( *e );
        m_events.push( stored );
        //}
    }
    return m_record;
}


/** Update, pop events in play mode */
void Recorder::Update(long dt)
{
    static bool  firstEvent = true;

    // Test to see if we are playing a demo
    if ( m_playing && !m_record && !m_events.empty() && g_kernel->GetTime() >= m_nextTime-m_deltaTime )
    {
        IEvent e = m_events.front();
        m_events.pop();

        // trigger the event
        EventManager::GetInstance()->TriggerEvent( e );

        if ( firstEvent )
        {
            if ( !m_events.empty() )
            {
                m_nextTime = g_kernel->GetTime() + m_events.front().GetTimeFired() - e.GetTimeFired(); 
                m_oldTime = e.GetTimeFired();
            }
        }
        else
        {
            m_deltaTime  = g_kernel->GetTime() - m_nextTime;
            m_nextTime   = g_kernel->GetTime() + e.GetTimeFired() - m_oldTime;
            m_oldTime    = e.GetTimeFired();
            firstEvent = false;
        }
        // we have reached the end of the demo
        if ( m_events.empty() ) 
        {
            m_playing       = false;
            firstEvent      = true;
            m_nextTime = m_oldTime = m_deltaTime = 0.0f;
        }
        
    }
}

/** Enable recording */
void Recorder::StartRecording(const std::string &fileName)
{
    Player* player = EntityManager::GetInstance()->GetPlayer();
    if ( !player )
    {
        g_kernel->LPrintf("--Error could not record, no Player availible!" );
        return;
    } 
    else
    {
        // remove all event contents
        m_events.c.clear();

        m_player.name   = player->GetName();
        m_player.state  = player->GetState();
        m_player.pos    = player->m_physObject.GetPosition();
        m_player.speed  = player->m_fSpeed;
        m_player.hp     = player->m_health;

        m_fileName      = fileName;
        m_startTime     = g_kernel->GetTime();
        m_record        = true;
        g_kernel->LPrintf("--Recording starting..." );
    }
}

/** Save to file */
void Recorder::Save()
{
    if ( !m_record || m_playing ) { return; }

    std::ofstream   stream;
    stream.open( (m_fileName + ".rcd").c_str() );

    /*---------------------------------------
        Write Header - TODO - write out in Binary
    ----------------------------------------*/
    stream  << m_player.name 
            << "," 
            << m_player.state 
            << ",\n"
            << m_player.pos.x 
            << "," 
            << m_player.pos.y 
            << ",\n"
            << m_player.hp  
            << "," 
            << m_player.speed; 

    // now write the actions
    while ( !m_events.empty() )
    {
        IEvent e = m_events.front();
        m_events.pop();

        stream  << "\n" 
                << e.GetType() 
                << "," 
                << (e.GetTimeFired() - m_startTime) 
                << "," 
                << e.GetData<EntityEvents>()->buttons
                << ","
                << e.GetData<EntityEvents>()->ent_id
                << "," 
                << e.GetListenerType();
    }
    stream.close();
    g_kernel->LPrintf("--Stop Recording..." );
}


/** Load File */
void Recorder::Load(const std::string &fileName)
{
    MFile file;
    // open stream
    if ( file.Open( fileName ) )
    {
        g_kernel->LPrintf( ("ERROR: Could not find: " + fileName).c_str() );
        return;
    }

    Player* player = EntityManager::GetInstance()->GetPlayer();
    /*------------------------------------
        Read Header
    -------------------------------------*/
    player->SetName ( file.ReadNext(",") );
    player->SetState( file.ReadNext(",") );

    float x = file.ReadDouble();
    float y = file.ReadDouble();
    Vector2f moveTo( x, y );


    player->MoveTo( moveTo );
    g_kernel->GetRenderer()->GetCamera(0)->SetPosition( moveTo );     // HACK FOR NOW - move the camera
    
    player->m_health = file.ReadInt();
    player->m_fSpeed = file.ReadDouble();

    m_events.c.clear();
    /*------------------------------------
        Read Body
    -------------------------------------*/
    while ( !file.IsEnd() )
    {
        EventType evtType       = (EventType)file.ReadInt();
        float time              =  file.ReadDouble();
        int buttons             =  file.ReadInt();
        unsigned int id         =  file.ReadInt();
        ListenerType listType   = (ListenerType)file.ReadInt();

        m_events.push( IEvent( evtType, time, new EntityEvents(id, buttons), listType ) );

    }
    file.Close();
    g_kernel->LPrintf("--Playing Recording..." );

}


/*--------------------------------------------------------------
    Recording Console Commands
---------------------------------------------------------------*/

void Record_f::Exec(std::string &s)
{
    if ( m_recorder )
    {
        m_recorder->StartRecording(s);
    }
}

void StopRecording_f::Exec(std::string &s)
{
    if ( m_recorder )
    {
        m_recorder->StopRecording();
    }
    
}

void PlayRecording_f::Exec(std::string &s)
{
    if ( m_recorder )
    {
        m_recorder->PlayRecording( s );
    }
    
}